package org.xenon.vote.service;

import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.xenon.vote.dao.imp.ChoiceDao;
import org.xenon.vote.dao.imp.JudgeDao;
import org.xenon.vote.dao.imp.TurnDao;
import org.xenon.vote.exception.BallotExistException;
import org.xenon.vote.exception.BallotStoppedException;
import org.xenon.vote.model.pojo.po.Choice;
import org.xenon.vote.model.pojo.po.Judge;
import org.xenon.vote.model.pojo.po.Turn;
import org.xenon.vote.model.pojo.vo.Board;

import java.util.*;

/**
 * 进行中的投票的管理器
 */
@Service
public class BallotManager {
    private final TurnDao turnDao;
    private final ChoiceDao choiceDao;
    private final JudgeDao judgeDao;
    /**
     * 正在进行的投票
     * <所有者, 投票>
     */
    private static final HashMap<String, Board> underway = new HashMap<>();

    public BallotManager(TurnDao turnDao, ChoiceDao choiceDao, JudgeDao judgeDao) {
        this.turnDao = turnDao;
        this.choiceDao = choiceDao;
        this.judgeDao = judgeDao;
    }

    /**
     * 给一个选项投票
     * @param name 裁判名字
     * @param id 选项ID
     */
    public void vote(String name, int id, String owner){
        Judge judge = judgeDao.get(name, owner);
        if (judge == null) return;
        Board board = underway.get(judge.getOwner());
        board.vote(name, id);

    }

    /**
     * 在投票场注册一个裁判
     *
     * @param judge 裁判
     */
    public void judgeLogin(Judge judge) {
        if (judge == null) return;
        String owner = judge.getOwner();
        Board board = underway.get(owner);
        if (board == null) return;
        if (board.getPresented().contains(judge)) return;
        board.attend(judge);
    }

    /**
     * 登出一个裁判
     *
     * @param name 裁判名字
     */
    public void judgeLogout(String name) {
        Judge judge = judgeDao.get(name);
        if (judge == null) return;
        String owner = judge.getOwner();
        Board board = underway.get(owner);
        board.drop(name);
    }

    /**
     * 获得某投票的所有选项
     *
     * @param mail 所有者
     */
    public List<String> getChoices(String mail) throws BallotStoppedException {
        Board board = underway.get(mail);
        if (board == null) return null;
        Collection<Choice> values;
        values = board.getRegedit().values();
        List<String> res = new ArrayList<>();
        for (Choice c : values)
            res.add(c.getDetail());
        return res;
    }

    /**
     * 直接删除一个投票
     *
     * @param mail 投票ID
     */
    public void drop(String mail) {
        underway.remove(mail);
    }

    /**
     * 停止投票, 存储结果, 返回结果
     *
     * @param mail 所有者
     * @return 下一轮的初始值
     */
    public HashMap<Choice, Integer> stop(String mail, int ballot) {
        HashMap<Choice, Integer> res = getResult(mail);
        if(res == null) return null;
        underway.remove(mail);
        int turn = turnDao.getAll(ballot).get(0).getId();
        int order = 0;
        for (Choice c : res.keySet()) {
            c.setScore(res.get(c));
            choiceDao.edit(c);
        }
        for (Choice c : res.keySet()) {
            c.setOrder(order++);
            c.setTurn(turn);
            choiceDao.add(c);
        }
        return res;
    }

    /**
     * 直接获取投票计分板
     *
     * @param mail 所有者
     * @return 计分板
     */
    public Board getBoard(String mail) {
        return underway.get(mail);
    }

    /**
     * 获得投票结果
     *
     * @param mail 所有者
     * @return 结果, 只包含选出的选项 如果在投票中, 则返回null 已排序好
     */
    public HashMap<Choice, Integer> getResult(String mail) {
        Board board = underway.get(mail);
        if (board == null) return null;
        HashMap<Choice, Integer> pri = board.getChoices();
        Asi[] asis = new Asi[pri.size()];
        int count = 0;
        for (Choice choice : pri.keySet())
            asis[count++] = new Asi(choice, pri.get(choice));
        Arrays.sort(asis);
        int limit = board.getLimit();
        HashMap<Choice, Integer> res = new HashMap<>();
        for (int i = 0; i < limit; i++)
            res.put(asis[i].choice, asis[i].score);
        return res;
    }

    /**
     * 裁判获取本人的投票状态
     *
     * @param name 裁判名字
     * @return 投票过的选项和总可投票数, null表明验证失败
     */
    public List<Integer> getStatus(String name, String owner) {
        Judge judge = judgeDao.get(name, owner);
        if (judge == null) return null;
        Board board = underway.get(judge.getOwner());
        if (board == null) return null;
        List<Integer> status = board.getStatus(name, judge.getOwner());
        status.add(board.getOption());
        return status;
    }

    /**
     * 向列表添加一个进行中的投票.
     *
     * @param turnId 轮次ID
     * @param mail   轮次的所有者
     * @throws BallotExistException 该账号已经存在一个进行中的投票
     */
    public void add(int turnId, String mail) throws BallotExistException {
        if (check(mail)) throw new BallotExistException();
        HashMap<Integer, Choice> regedit = new HashMap<>();
        Turn turn = turnDao.get(turnId);
        addAsi(turn, mail, regedit);
    }

    public void add(Turn turn, String mail) throws BallotExistException {
        if (check(mail)) throw new BallotExistException();
        HashMap<Integer, Choice> regedit = new HashMap<>();
        addAsi(turn, mail, regedit);
    }

    /**
     * 两个add方法的共有部分, 提取至此
     */
    private void addAsi(Turn turn, String mail, HashMap<Integer, Choice> regedit) {
        List<Choice> allChoice = choiceDao.getAll(turn.getId());
        for (Choice c : allChoice)
            regedit.put(c.getId(), c);
        List<Judge> allJudge = judgeDao.getAll(turn.getId());
        HashMap<String, Judge> shouldPresent = new HashMap<>();
        for (Judge j : allJudge)
            shouldPresent.put(j.getName(), j);
        Board board = new Board(regedit, shouldPresent, turn.getId(), turn.getLimit(), turn.getOption());
        underway.put(mail, board);
    }


    /**
     * 检查某账号是否有在进行的投票
     *
     * @param mail 账号
     * @return 如果有正在进行的 返回true
     */
    public boolean check(String mail) {
        Board board = underway.get(mail);
        return board != null;
    }

    /**
     * 选项与名次的对应
     */
    private static class Asi implements Comparable<Asi> {
        private final Choice choice;
        private final int score;

        public Asi(Choice choice, int score) {
            this.choice = choice;
            this.score = score;
        }

        @Override
        public int compareTo(@NotNull BallotManager.Asi o) {
            return -(this.score - o.score);
        }
    }
}
